package com.panfeng.xcloud.boss.provider.assets.service.exchange.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.binance.client.model.enums.CandlestickInterval;
import com.binance.client.model.market.Candlestick;
import com.okex.open.api.bean.account.param.SetLeverage;
import com.okex.open.api.bean.trade.param.PlaceOrder;
import com.okex.open.api.config.APIConfiguration;
import com.okex.open.api.enums.I18nEnum;
import com.okex.open.api.service.account.impl.AccountAPIServiceImpl;
import com.okex.open.api.service.marketData.impl.MarketDataAPIServiceImpl;
import com.okex.open.api.service.publicData.impl.PublicDataAPIServiceImpl;
import com.okex.open.api.service.trade.impl.TradeAPIServiceImpl;
import com.panfeng.xcloud.boss.provider.assets.dao.mapper.CoinListMapperExt;
import com.panfeng.xcloud.boss.provider.assets.dao.mapper.UserExchangePwdMapperExt;
import com.panfeng.xcloud.boss.provider.assets.service.exchange.IExchangeMethodService;
import com.panfeng.xcloud.boss.provider.assets.utils.OrderNoGenerate;
import com.panfeng.xcloud.boss.provider.dto.request.OperateBotReqDTO;
import com.panfeng.xcloud.boss.provider.dto.request.SendEmailReqDTO;
import com.panfeng.xcloud.boss.provider.rpc.member.UserInfoFeignApi;
import com.panfeng.xcloud.common.core.candle.CandleEntry;
import com.panfeng.xcloud.common.core.candle.MyCandleEntry;
import com.panfeng.xcloud.common.core.configuration.DataDictionaryConfig;
import com.panfeng.xcloud.common.core.enums.*;
import com.panfeng.xcloud.common.core.exceptions.BaseBizException;
import com.panfeng.xcloud.common.core.utils.DES3Util;
import com.panfeng.xcloud.common.core.utils.KeyContactUtil;
import com.panfeng.xcloud.common.core.web.vo.InnerTransferVO;
import com.panfeng.xcloud.common.core.web.vo.OrderDetailResponseVO;
import com.panfeng.xcloud.common.core.web.vo.TradeResponseVO;
import com.panfeng.xcloud.dao.assets.entity.CoinList;
import com.panfeng.xcloud.dao.member.entity.UserExchangePwd;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.util.*;

@Service
@Slf4j
public class OkexMethodService implements IExchangeMethodService {

    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private DataDictionaryConfig dataDictionaryConfig;

    @Autowired
    private UserExchangePwdMapperExt userExchangePwdMapperExt;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private UserInfoFeignApi userInfoFeignApi;

    @Autowired
    private CoinListMapperExt coinListMapperExt;

    @Autowired
    @Qualifier(value = "notLoadBalancedRestTemplate")
    private RestTemplate restTemplate;

    @Value("${xdcloud.des3.secret-key}")
    private String secrectKey;

    /**
     * 查询账户总可用余额
     *
     * @param userId
     * @param valuationAsset
     * @return
     */
    @Override
    public BigDecimal getTotalAvaliableMoney(String userId, String valuationAsset) {
        AccountAPIServiceImpl accountAPIService = getAccountAPIService(userId);
        JSONObject balanceResult = accountAPIService.getBalance(CoinTypeEnum.USDT.getCode());
        JSONObject data = (JSONObject) balanceResult.getJSONArray("data").get(0);
        JSONObject details = (JSONObject) data.getJSONArray("details").get(0);
        BigDecimal totalAvaliableMoney = details.getBigDecimal("availEq");
        return totalAvaliableMoney;
    }

    /**
     * 查询某交易对保证金率
     *
     * @param userId
     * @param contractCode
     * @param direction
     * @return
     */
    @Override
    public BigDecimal getRiskRate(String userId, String contractCode, String direction) {
        AccountAPIServiceImpl accountAPIService = getAccountAPIService(userId);
        JSONObject balanceResult = accountAPIService.getBalance(CoinTypeEnum.USDT.getCode());
        JSONObject data = (JSONObject) balanceResult.getJSONArray("data").get(0);
        JSONObject details = (JSONObject) data.getJSONArray("details").get(0);
        BigDecimal riskRate = details.getBigDecimal("mgnRatio");
        return riskRate;
    }

    /**
     * 查询某交易对的可用仓位
     *
     * @param userId
     * @param contractCode
     * @param direction
     * @param leverRate
     * @return
     */
    @Override
    public BigDecimal getAvaliablePosition(String userId, String contractCode, String direction, Integer leverRate) {
        String myPosSide = direction.equals(PositionSideTypeEnum.BUY.getCode()) ? "long" : "short";
        AccountAPIServiceImpl accountAPIService = getAccountAPIService(userId);
        JSONObject swap = accountAPIService.getPositions("SWAP", contractCode, null);
        JSONArray data = swap.getJSONArray("data");
        BigDecimal avaliablePosition = BigDecimal.ZERO;
        for (Object datum : data) {
            JSONObject obj = (JSONObject) datum;
            String posSide = obj.getString("posSide");
            if (posSide.equals(myPosSide)) {
                String[] pair = contractCode.split("-");
                String destinationCoin = pair[0];
                String sourceCoin = pair[1];
                CoinList coinList = coinListMapperExt.queryCoinListByDestinationCoinAndSourceCoin(ExchangeTypeEnum.OKEX.getCode(),
                        destinationCoin, sourceCoin, CoinListStatusTypeEnum.OPEN.code, direction);
                BigDecimal contractCodeValue = coinList.getContractCodeValue();
                BigDecimal availPos = obj.getBigDecimal("availPos");
                avaliablePosition = availPos == null ? BigDecimal.ZERO : availPos.multiply(contractCodeValue);
                break;
            }
        }
        return avaliablePosition;
    }

    @Override
    public BigDecimal getPositionAgvPrice(String userId, String contractCode, String direction, Integer leverRate) {
        direction = direction.equals(PositionSideTypeEnum.BUY.getCode()) ? "long" : "short";
        AccountAPIServiceImpl accountAPIService = getAccountAPIService(userId);
        JSONObject swap = accountAPIService.getPositions("SWAP", contractCode, null);
        JSONArray data = swap.getJSONArray("data");
        BigDecimal positionAvgPrice = BigDecimal.ZERO;
        for (Object datum : data) {
            JSONObject obj = (JSONObject) datum;
            String posSide = obj.getString("posSide");
            if (posSide.equals(direction)) {
                positionAvgPrice = obj.getBigDecimal("avgPx");
                break;
            }
        }
        return positionAvgPrice == null ? BigDecimal.ZERO : positionAvgPrice;
    }

    /**
     * 查询某交易对的可用余额
     *
     * @param userId
     * @param contractCode
     * @return
     */
    @Override
    public BigDecimal getAvaliableMoney(String userId, String contractCode) {
        BigDecimal avaliableMoney = getTotalAvaliableMoney(userId, ValuationAssetTypeEnum.USD.getCode());
        return avaliableMoney;
    }

    /**
     * 查询某交易对的可开数
     *
     * @param userId
     * @param destinationCoin
     * @param sourceCoin
     * @param direction
     * @param leverRate
     * @return
     */
    @Override
    public BigDecimal getAvaliablePieces(String userId, String destinationCoin, String sourceCoin, String direction, Integer leverRate) {
        String contractCode = KeyContactUtil.getContractCode(ExchangeTypeEnum.OKEX.getCode(), destinationCoin, sourceCoin);
        //该交易对可用余额
        BigDecimal availableMoney = getAvaliableMoney(userId, contractCode).multiply(new BigDecimal(leverRate));
        return availableMoney;
    }


    /**
     * 交易所下市价单
     *
     * @param operateBotReqDTO
     * @return
     */
    @Override
    public TradeResponseVO trade(OperateBotReqDTO operateBotReqDTO) {
        if (operateBotReqDTO.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
            throw new BaseBizException(ResponseStatusEnum.TRADER_AMOUNT_CANT_SMALL_THAN_ZERO);
        }
        TradeResponseVO tradeResponseVO = new TradeResponseVO();
        String contractCode = KeyContactUtil.getContractCode(operateBotReqDTO.getExchangeType(), operateBotReqDTO.getDestinationCoin(), operateBotReqDTO.getSourceCoin());
        TradeAPIServiceImpl tradeAPIService = getTradeAPIService(operateBotReqDTO.getUserId());
        BigDecimal oldAvaliablePosition = null;
        BigDecimal newAvaliablePosition = null;
        String newClientOrderId = OrderNoGenerate.getOrderIdByTime(operateBotReqDTO.getUserId());
        PlaceOrder placeOrder = new PlaceOrder();
        placeOrder.setInstId(contractCode);
        placeOrder.setTdMode("cross");
        placeOrder.setCcy(CoinTypeEnum.USDT.getCode());
        placeOrder.setOrdType("market");
        try {
            if (operateBotReqDTO.getOperateType() == OperateTypeEnum.BUY.getCode()) {
                oldAvaliablePosition = mustGetAvaliablePositionInTrade(operateBotReqDTO.getUserId(), contractCode, operateBotReqDTO.getDirection(), operateBotReqDTO.getLeverRate());
                placeOrder.setSz(operateBotReqDTO.getAmount().toString());
                if (operateBotReqDTO.getDirection().equalsIgnoreCase(PositionSideTypeEnum.BUY.getCode())) {
                    placeOrder.setPosSide("long");
                    placeOrder.setSide("buy");
                } else if (operateBotReqDTO.getDirection().equalsIgnoreCase(PositionSideTypeEnum.SELL.getCode())) {
                    placeOrder.setPosSide("short");
                    placeOrder.setSide("sell");
                }
                placeOrder.setClOrdId(newClientOrderId);
                JSONObject result = tradeAPIService.placeOrder(placeOrder);
                JSONObject data = (JSONObject) result.getJSONArray("data").get(0);
                tradeResponseVO.setNewClientOrderId(newClientOrderId);
            }
        } catch (Exception e) {
            logger.error("双向王trade方法开仓时报错", e);
            newAvaliablePosition = mustGetAvaliablePositionInTrade(operateBotReqDTO.getUserId(), contractCode, operateBotReqDTO.getDirection(), operateBotReqDTO.getLeverRate());
            if (oldAvaliablePosition.add(operateBotReqDTO.getAmount()).compareTo(newAvaliablePosition) == 0) {
                String message = "双向王trade开仓时报错但实际交易所下单成功   用户id:" + operateBotReqDTO.getUserId()
                        + "   方向:" + operateBotReqDTO.getDirection()
                        + "   倍数:" + operateBotReqDTO.getLeverRate()
                        + "   交易对:" + contractCode
                        + "   异常信息:" + com.panfeng.xcloud.common.core.utils.StringUtils.printStackTraceToString(e);
                userInfoFeignApi.sendEmail(new SendEmailReqDTO(message));
                tradeResponseVO.setNewClientOrderId(newClientOrderId);
            } else {
                String message = "双向王trade开仓时报错实际交易所下单也失败   用户id:" + operateBotReqDTO.getUserId()
                        + "   方向:" + operateBotReqDTO.getDirection()
                        + "   倍数:" + operateBotReqDTO.getLeverRate()
                        + "   交易对:" + contractCode
                        + "   异常信息:" + com.panfeng.xcloud.common.core.utils.StringUtils.printStackTraceToString(e);
                userInfoFeignApi.sendEmail(new SendEmailReqDTO(message));
                throw e;
            }
        }

        try {
            if (operateBotReqDTO.getOperateType() == OperateTypeEnum.SELL.getCode()) {
                CoinList coinList = coinListMapperExt.queryCoinListByDestinationCoinAndSourceCoin(ExchangeTypeEnum.OKEX.getCode(),
                        operateBotReqDTO.getDestinationCoin(), operateBotReqDTO.getSourceCoin(), CoinListStatusTypeEnum.OPEN.code, operateBotReqDTO.getDirection());
                BigDecimal contractCodeValue = coinList.getContractCodeValue();
                placeOrder.setSz(operateBotReqDTO.getAmount().divide(contractCodeValue, 1, BigDecimal.ROUND_DOWN).toString());
                if (operateBotReqDTO.getDirection().equalsIgnoreCase(PositionSideTypeEnum.BUY.getCode())) {
                    placeOrder.setPosSide("long");
                    placeOrder.setSide("sell");
                } else if (operateBotReqDTO.getDirection().equalsIgnoreCase(PositionSideTypeEnum.SELL.getCode())) {
                    placeOrder.setPosSide("short");
                    placeOrder.setSide("buy");
                }
                JSONObject result = tradeAPIService.placeOrder(placeOrder);
                JSONObject data = (JSONObject) result.getJSONArray("data").get(0);
                tradeResponseVO.setOrderId(data.getLong("ordId"));
            }
        } catch (Exception e) {
            logger.error("双向王trade方法平仓时报错", e);
            String message = "双向王trade平仓时报错   用户id:" + operateBotReqDTO.getUserId()
                    + "   方向:" + operateBotReqDTO.getDirection()
                    + "   倍数:" + operateBotReqDTO.getLeverRate()
                    + "   交易对:" + contractCode
                    + "   异常信息:" + com.panfeng.xcloud.common.core.utils.StringUtils.printStackTraceToString(e);
            userInfoFeignApi.sendEmail(new SendEmailReqDTO(message));
            throw e;
        }

        tradeResponseVO.setCreatedAt(new Date().getTime());

        return tradeResponseVO;
    }

    /**
     * 获取订单详情
     *
     * @param operateBotReqDTO
     * @param tradeResponseVO
     * @return
     */
    @Override
    public OrderDetailResponseVO getOrderDetail(OperateBotReqDTO operateBotReqDTO, TradeResponseVO tradeResponseVO) {
        String contractCode = KeyContactUtil.getContractCode(operateBotReqDTO.getExchangeType(), operateBotReqDTO.getDestinationCoin(), operateBotReqDTO.getSourceCoin());
        TradeAPIServiceImpl tradeAPIService = getTradeAPIService(operateBotReqDTO.getUserId());
        JSONObject data = null;
        while (data == null) {
            try {
                //获取订单详情
                if (tradeResponseVO.getOrderId() == null || tradeResponseVO.getOrderId() == 0l) {
                    JSONObject result = tradeAPIService.getOrderDetails(contractCode, null, tradeResponseVO.getNewClientOrderId());
                    data = (JSONObject) result.getJSONArray("data").get(0);
                } else {
                    JSONObject result = tradeAPIService.getOrderDetails(contractCode, String.valueOf(tradeResponseVO.getOrderId()), null);
                    data = (JSONObject) result.getJSONArray("data").get(0);
                }
            } catch (Exception e) {
                logger.error("双向王getOrderDetail方法疯狂循环中", e);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
            }
        }

        CoinList coinList = coinListMapperExt.queryCoinListByDestinationCoinAndSourceCoin(ExchangeTypeEnum.OKEX.getCode(),
                operateBotReqDTO.getDestinationCoin(), operateBotReqDTO.getSourceCoin(), CoinListStatusTypeEnum.OPEN.code, operateBotReqDTO.getDirection());
        BigDecimal contractCodeValue = coinList.getContractCodeValue();
        OrderDetailResponseVO orderDetailResponseVO = new OrderDetailResponseVO();
        orderDetailResponseVO.setContractCode(data.getString("instId"));
        orderDetailResponseVO.setLeverRate(operateBotReqDTO.getLeverRate());
        orderDetailResponseVO.setDirection(operateBotReqDTO.getDirection());
        orderDetailResponseVO.setVolume(data.getBigDecimal("sz").multiply(contractCodeValue));
        orderDetailResponseVO.setVolumeNum(data.getBigDecimal("sz").multiply(contractCodeValue));
        orderDetailResponseVO.setPrice(data.getBigDecimal("avgPx"));
        orderDetailResponseVO.setCreatedAt(tradeResponseVO.getCreatedAt());
        orderDetailResponseVO.setFeeAsset(data.getString("feeCcy"));
        orderDetailResponseVO.setOrderId(data.getLong("ordId"));
        orderDetailResponseVO.setNewClientOrderId(data.getString("clOrdId"));
        orderDetailResponseVO.setTradeAvgPrice(data.getBigDecimal("avgPx"));
        orderDetailResponseVO.setTradeTurnover(orderDetailResponseVO.getVolume().multiply(orderDetailResponseVO.getTradeAvgPrice()));
        orderDetailResponseVO.setTradeVolume(orderDetailResponseVO.getVolume());
        orderDetailResponseVO.setTradeVolumeNum(orderDetailResponseVO.getVolumeNum());
        orderDetailResponseVO.setRealProfit(data.getBigDecimal("pnl"));
        orderDetailResponseVO.setFee(data.getBigDecimal("fee").abs());

        return orderDetailResponseVO;
    }

    /**
     * 同账户内资金划转
     *
     * @param innerTransferVO
     * @return
     */
    @Override
    public String transferInner(InnerTransferVO innerTransferVO) {
        return null;
    }

    /**
     * 获取展示市价
     *
     * @param userId
     * @param destinationCoin
     * @param sourceCoin
     * @return
     */
    @Override
    public BigDecimal getAppearMarketPrice(String userId, String destinationCoin, String sourceCoin) {
        String contractCode = KeyContactUtil.getContractCode(ExchangeTypeEnum.OKEX.getCode(), destinationCoin, sourceCoin);
        PublicDataAPIServiceImpl publicDataAPIService = getPublicDataAPIService(userId);
        JSONObject swap = publicDataAPIService.getMarkPrice("SWAP", null, contractCode);
        JSONObject data = (JSONObject) swap.getJSONArray("data").get(0);
        BigDecimal markPrice = data.getBigDecimal("markPx");
        redisTemplate.opsForValue().set(KeyContactUtil.getAppearMarketKey(ExchangeTypeEnum.OKEX.getCode(), destinationCoin, sourceCoin), String.valueOf(markPrice));
        return markPrice;
    }

    /**
     * 获取行情深度,供计算使用
     *
     * @param userId
     * @param destinationCoin
     * @param sourceCoin
     * @return
     */
    @Override
    public JSONObject getMarketPriceDepth(String userId, String destinationCoin, String sourceCoin) {
        String contractCode = KeyContactUtil.getContractCode(ExchangeTypeEnum.OKEX.getCode(), destinationCoin, sourceCoin);
        MarketDataAPIServiceImpl marketDataAPIService = getMarketDataAPIService(userId);
        JSONObject orderBook = marketDataAPIService.getOrderBook(contractCode, "400");
        JSONObject depth = (JSONObject) orderBook.getJSONArray("data").get(0);
        return depth;
    }

    /**
     * 判断杠杆倍数是否可用
     *
     * @param userId
     * @param destinationCoin
     * @param sourceCoin
     * @param leverRate
     * @return
     */
    @Override
    public boolean leverRateCanUse(String userId, String destinationCoin, String sourceCoin, Integer leverRate) {
        String[] split = {
                "1", "2", "3", "4", "5", "6", "7", "8", "9",
                "10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
                "20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
                "30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
                "40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
                "50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
                "60", "61", "62", "63", "64", "65", "66", "67", "68", "69",
                "70", "71", "72", "73", "74", "75", "100"};
        return Arrays.asList(split).contains(String.valueOf(leverRate));
    }

    /**
     * 调用接口调整杠杆倍数
     *
     * @param userId
     * @param destinationCoin
     * @param sourceCoin
     * @param leverRate
     * @return
     */
    @Override
    public boolean changeLeverRate(String userId, String destinationCoin, String sourceCoin, Integer leverRate) {
        String contractCode = KeyContactUtil.getContractCode(ExchangeTypeEnum.OKEX.getCode(), destinationCoin, sourceCoin);
        SetLeverage setLeverage = new SetLeverage();
        setLeverage.setInstId(contractCode);
        setLeverage.setLever(String.valueOf(leverRate));
        setLeverage.setMgnMode("cross");
        AccountAPIServiceImpl accountAPIService = getAccountAPIService(userId);
        JSONObject result = accountAPIService.setLeverage(setLeverage);
        String code = result.getString("code");
        if (code.equalsIgnoreCase("0")) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 获取K线数据
     *
     * @param exchangeType
     * @param userId
     * @param destinationCoin
     * @param sourceCoin
     * @param interval
     * @param startTime
     * @param endTime
     * @param limit
     * @return
     */
    @Override
    public JSONObject getCandleStick(Integer exchangeType, String userId, String destinationCoin, String sourceCoin, CandlestickInterval interval, Long startTime, Long endTime, int limit) {
        String period = "1m";
        long duration = 0l;
        switch (interval.toString()) {
            case "1m":
                period = "1m";
                duration = 60000l;
                break;
            case "5m":
                period = "5m";
                duration = 300000l;
                break;
            case "15m":
                period = "15m";
                duration = 900000l;
                break;
            case "30m":
                period = "30m";
                duration = 1800000l;
                break;
            case "1h":
                period = "1H";
                duration = 3600000l;
                break;
            case "4h":
                period = "4H";
                duration = 14400000l;
                break;
            case "1d":
                period = "1D";
                duration = 86400000l;
                break;
            case "1w":
                period = "1W";
                duration = 604800000l;
                break;
            case "1M":
                period = "1M";
                duration = 2592000000l;
                break;
            default:
                period = "1m";
                duration = 60000l;
                break;
        }
        String contractCode = KeyContactUtil.getContractCode(ExchangeTypeEnum.OKEX.getCode(), destinationCoin, sourceCoin);
        MarketDataAPIServiceImpl marketDataAPIService = getMarketDataAPIService(userId);
        String after = endTime == null ? null : String.valueOf(endTime);
        String before = startTime == null ? null : String.valueOf(startTime);
        JSONObject result = marketDataAPIService.getCandlesticks(contractCode, after, before, period, String.valueOf(limit));
        JSONArray reqData = result.getJSONArray("data");
        List<Candlestick> candles = packCandles(reqData,duration);
        List<CandleEntry> entries = new ArrayList<>();
        for (int i = candles.size() - 1; i >= 0; i--) {
            Candlestick candlestick = candles.get(i);
            entries.add(new MyCandleEntry(i, candlestick.getHigh().floatValue(), candlestick.getLow().floatValue(), candlestick.getOpen().floatValue(), candlestick.getClose().floatValue(), candlestick.getVolume().floatValue(), candlestick.getOpenTime(), candlestick.getCloseTime(), candlestick.getQuoteAssetVolume()));
        }
        JSONArray candlesticks = JSONArray.parseArray(JSONArray.toJSONString(entries));
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("candlesticks", candlesticks);

        return jsonObject;
    }

    //---------------------------------------------------------------------分隔符-------------------------------------------------------------------------------------
    private AccountAPIServiceImpl getAccountAPIService(String userId) {
        String[] apiKeyAndSecretKey = getKeys(userId);
        String secretKey = apiKeyAndSecretKey[0];
        String apiKey = apiKeyAndSecretKey[1];
        String passpharse = apiKeyAndSecretKey[2];
        String restHost = dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.okex_rest_host.getCode());

        APIConfiguration config = new APIConfiguration();
        config.setEndpoint(restHost);
        config.setApiKey(apiKey);
        config.setSecretKey(secretKey);
        config.setPassphrase(passpharse);
        config.setPrint(true);
        config.setI18n(I18nEnum.ENGLISH);

        AccountAPIServiceImpl accountService = new AccountAPIServiceImpl(config);
        return accountService;
    }

    private TradeAPIServiceImpl getTradeAPIService(String userId) {
        String[] apiKeyAndSecretKey = getKeys(userId);
        String secretKey = apiKeyAndSecretKey[0];
        String apiKey = apiKeyAndSecretKey[1];
        String passpharse = apiKeyAndSecretKey[2];
        String restHost = dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.okex_rest_host.getCode());

        APIConfiguration config = new APIConfiguration();
        config.setEndpoint(restHost);
        config.setApiKey(apiKey);
        config.setSecretKey(secretKey);
        config.setPassphrase(passpharse);
        config.setPrint(true);
        config.setI18n(I18nEnum.ENGLISH);

        TradeAPIServiceImpl tradeService = new TradeAPIServiceImpl(config);
        return tradeService;
    }

    private PublicDataAPIServiceImpl getPublicDataAPIService(String userId) {
        String[] apiKeyAndSecretKey = getKeys(userId);
        String secretKey = apiKeyAndSecretKey[0];
        String apiKey = apiKeyAndSecretKey[1];
        String passpharse = apiKeyAndSecretKey[2];
        String restHost = dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.okex_rest_host.getCode());

        APIConfiguration config = new APIConfiguration();
        config.setEndpoint(restHost);
        config.setApiKey(apiKey);
        config.setSecretKey(secretKey);
        config.setPassphrase(passpharse);
        config.setPrint(true);
        config.setI18n(I18nEnum.ENGLISH);

        PublicDataAPIServiceImpl publicDataAPIService = new PublicDataAPIServiceImpl(config);
        return publicDataAPIService;
    }

    private MarketDataAPIServiceImpl getMarketDataAPIService(String userId) {
        String[] apiKeyAndSecretKey = getKeys(userId);
        String secretKey = apiKeyAndSecretKey[0];
        String apiKey = apiKeyAndSecretKey[1];
        String passpharse = apiKeyAndSecretKey[2];
        String restHost = dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.okex_rest_host.getCode());

        APIConfiguration config = new APIConfiguration();
        config.setEndpoint(restHost);
        config.setApiKey(apiKey);
        config.setSecretKey(secretKey);
        config.setPassphrase(passpharse);
        config.setPrint(true);
        config.setI18n(I18nEnum.ENGLISH);

        MarketDataAPIServiceImpl marketDataAPIService = new MarketDataAPIServiceImpl(config);
        return marketDataAPIService;
    }

    private String[] getKeys(String userId) {
        String[] keys = {"", "", ""};
        List<UserExchangePwd> userExchangePwd = userExchangePwdMapperExt.queryByUserId(userId, ExchangeTypeEnum.OKEX.getCode());

        if (userExchangePwd.size() != 3)
            throw new BaseBizException(ResponseStatusEnum.QUAN_BOT_OKEX_API_KEY_NOT_BIND);

        //API KEY 解密
        for (UserExchangePwd userExchangePwdAsset : userExchangePwd) {
            if (userExchangePwdAsset.getExchangeType().equals(ExchangeTypeEnum.OKEX.getCode())) {
                String salt = userExchangePwdAsset.getSalt();
                Integer pwdType = userExchangePwdAsset.getPwdType();
                String pwd = userExchangePwdAsset.getPwd();
                String decode = DES3Util.doubleDecode(pwd, secrectKey, salt);
                if (ExchangePwdTypeEnum.secretKey.code == pwdType) {
                    keys[0] = decode;
                } else if (ExchangePwdTypeEnum.apiKey.code == pwdType) {
                    keys[1] = decode;
                } else if (ExchangePwdTypeEnum.passphrase.code == pwdType) {
                    keys[2] = decode;
                }
            }
        }
        return keys;
    }

    private List<Candlestick> packCandles(JSONArray reqData, Long duration) {
        List<Candlestick> result = new LinkedList();
        for (Object reqDatum : reqData) {
            Candlestick element = new Candlestick();
            JSONArray data = (JSONArray) reqDatum;

            Long openTime = Long.valueOf((String) data.get(0));
            Long closeTime = openTime + duration;
            element.setOpenTime(openTime);
            element.setOpen(new BigDecimal((String) data.get(1)));
            element.setHigh(new BigDecimal((String) data.get(2)));
            element.setLow(new BigDecimal((String) data.get(3)));
            element.setClose(new BigDecimal((String) data.get(4)));
            element.setVolume(new BigDecimal((String) data.get(6)));
            element.setCloseTime(closeTime);
            element.setQuoteAssetVolume(new BigDecimal((String) data.get(7)));
            result.add(element);
        }
        return result;
    }

    private BigDecimal mustGetAvaliablePositionInTrade(String userId, String contractCode, String direction, Integer leverRate) {
        BigDecimal avaliablePosition = null;
        while (avaliablePosition == null) {
            try {
                avaliablePosition = getAvaliablePosition(userId, contractCode, direction, leverRate);
            } catch (Exception e) {
                logger.error("双向王trade方法中获取仓位时报错", e);
                String message = "双向王trade方法中获取仓位时报错  用户id:" + userId
                        + "   方向:" + direction
                        + "   倍数:" + leverRate
                        + "   交易对:" + contractCode
                        + "   异常信息:" + com.panfeng.xcloud.common.core.utils.StringUtils.printStackTraceToString(e);
                userInfoFeignApi.sendEmail(new SendEmailReqDTO(message));
            }
        }
        return avaliablePosition;
    }

}
